#include "fm33le0xx_fl.h"
#include "sys_hardware.h"
#include "string.h"

#define PLL_TIMEOUT 0xFFFFFFFFU
static clock_cb_t timer_4hz_cb = NULL;

void RCC_PLL_ConfigDomain_SYS(uint32_t Source, uint32_t PLL_R, uint32_t PLL_DB, uint32_t PLL_O) {
    MODIFY_REG(RCC->PLLCR, RCC_PLLCR_DB_Msk | RCC_PLLCR_REFPRSC_Msk | RCC_PLLCR_OSEL_Msk | RCC_PLLCR_INSEL_Msk,
               ((PLL_DB - 1) << RCC_PLLCR_DB_Pos) | PLL_R | PLL_O | Source);
}

void sys_select_rchf_to_pll(uint8_t freq) {
    FL_RCC_RCHF_WriteTrimValue(RCHF8M_TRIM);
    FL_RCC_RCHF_SetFrequency(FL_RCC_RCHF_FREQUENCY_8MHZ);
    FL_RCC_RCHF_Enable();
    FL_FLASH_SetReadWait(FLASH, FL_FLASH_READ_WAIT_2CYCLE);
    RCC_PLL_ConfigDomain_SYS(FL_RCC_PLL_CLK_SOURCE_RCHF, FL_RCC_PLL_PSC_DIV8, freq, FL_RCC_PLL_OUTPUT_X1);
    FL_RCC_PLL_Enable(); // 使能PLL
    while(FL_RCC_IsActiveFlag_PLLReady() != FL_SET); // 等待PLL建立稳定，才能将系统时钟源切到PLL

    FL_RCC_SetAHBPrescaler(FL_RCC_AHBCLK_PSC_DIV1);
    FL_RCC_SetAPB1Prescaler(FL_RCC_APB1CLK_PSC_DIV1);
    FL_RCC_SetAPB2Prescaler(FL_RCC_APB2CLK_PSC_DIV1);
    FL_RCC_SetSystemClockSource(FL_RCC_SYSTEM_CLK_SOURCE_PLL);

    if (freq <= 24) {
        FL_FLASH_SetReadWait(FLASH, FL_FLASH_READ_WAIT_0CYCLE);
    } else if ((freq > 24) && (freq <= 48)) {
        FL_FLASH_SetReadWait(FLASH, FL_FLASH_READ_WAIT_1CYCLE);
    } else {
        FL_FLASH_SetReadWait(FLASH, FL_FLASH_READ_WAIT_2CYCLE);
    }

    SystemCoreClockUpdate();
}

void sys_select_lp_clk(void) {
    FL_RCC_SetSystemClockSource(FL_RCC_SYSTEM_CLK_SOURCE_LPOSC); 
    FL_FLASH_SetReadWait(FLASH, FL_FLASH_READ_WAIT_1CYCLE);
    FL_RCC_SetAHBPrescaler(FL_RCC_AHBCLK_PSC_DIV1);
    FL_RCC_SetAPB1Prescaler(FL_RCC_APB1CLK_PSC_DIV1);
    FL_RCC_SetAPB2Prescaler(FL_RCC_APB2CLK_PSC_DIV1);

    SystemCoreClockUpdate();
}

void sys_select_rchf_8m_clk(void) {
    FL_RCC_RCHF_SetFrequency(FL_RCC_RCHF_FREQUENCY_8MHZ);
    FL_RCC_RCHF_Enable();
    FL_RCC_SetSystemClockSource(FL_RCC_SYSTEM_CLK_SOURCE_RCHF); 
    FL_FLASH_SetReadWait(FLASH, FL_FLASH_READ_WAIT_0CYCLE);
    FL_RCC_SetAHBPrescaler(FL_RCC_AHBCLK_PSC_DIV1);
    FL_RCC_SetAPB1Prescaler(FL_RCC_APB1CLK_PSC_DIV1);
    FL_RCC_SetAPB2Prescaler(FL_RCC_APB2CLK_PSC_DIV1);
    FL_RCC_PLL_Disable();
    SystemCoreClockUpdate();
}

void sys_clock_init(void) {
    FL_RCC_XTLF_SetWorkCurrent(FL_RCC_XTLF_WORK_CURRENT_150NA);    /* 设置工作电流为最大以便快速起振 */
    FL_RCC_XTLF_Enable();
    FL_RCC_SetLSCLKClockSource(FL_RCC_LSCLK_CLK_SOURCE_XTLF);

    FL_RCC_EnableGroup1BusClock(FL_RCC_GROUP1_BUSCLK_RTC);
    FL_RTC_WriteAdjustValue(RTC, 0);

    FL_LPTIM32_InitTypeDef lptim32;
    lptim32.clockSource = FL_RCC_LPTIM32_CLK_SOURCE_LPOSC;
    lptim32.prescalerClockSource = FL_LPTIM32_CLK_SOURCE_INTERNAL;
    lptim32.prescaler = FL_LPTIM32_PSC_DIV32;
    lptim32.autoReload = 0xffffffff;
    lptim32.mode = FL_LPTIM32_OPERATION_MODE_NORMAL;
    lptim32.onePulseMode = FL_LPTIM32_ONE_PULSE_MODE_CONTINUOUS;
    lptim32.countEdge = FL_LPTIM32_ETR_COUNT_EDGE_RISING;
    lptim32.triggerEdge = FL_LPTIM32_ETR_TRIGGER_EDGE_RISING;
    FL_LPTIM32_Init(LPTIM32, &lptim32);
    FL_LPTIM32_Enable(LPTIM32);

    timer_4hz_cb = NULL;
}

uint32_t sys_clock_get_ms(void) {
    static uint32_t last_ms = 0;
    uint32_t now_value = LPTIM32->CNT;
    if (now_value > last_ms) {
        last_ms = now_value;  
    }
    return last_ms;
}

void sys_clock_enable_4hz_cb(clock_cb_t cb) {
    timer_4hz_cb = cb;
    FL_RTC_EnableIT_4Hz(RTC);
    FL_NVIC_ConfigTypeDef    InterruptConfigStruct;
    InterruptConfigStruct.preemptPriority = 0;
    FL_NVIC_Init(&InterruptConfigStruct, RTC_IRQn);
}

void sys_clock_disable_it() {
    FL_RTC_DisableIT_4Hz(RTC);
}

void sys_clock_delay_ms(uint32_t ms) {
    uint32_t ticks_start = sys_clock_get_ms();
    while(sys_clock_get_ms() - ticks_start < ms);
}

//获取RTC模块的时间到 ram
uint8_t rtc_get_time(FL_RTC_InitTypeDef *InitStructer) {
    uint8_t n, i;
    uint8_t result = 1;

    FL_RTC_InitTypeDef TempTime1, TempTime2;
    for(n = 0 ; n < 3; n++) {
        FL_RTC_GetTime(RTC, &TempTime1);                      // 读一次时间
        FL_RTC_GetTime(RTC, &TempTime2);                      // 再读一次时间

        for(i = 0; i < 7; i++) {                              // 两者一致, 表示读取成功
            if(((uint32_t *)(&TempTime1))[i] != ((uint32_t *)(&TempTime2))[i]) { break; }
        }

        if(i == 7) {
            result = 0;
            memcpy((uint32_t *)(InitStructer), (uint32_t *)(&TempTime1), 7 * sizeof(uint32_t)); //读取正确则更新新的时间
            break;
        }
    }

    return result;
}

uint8_t rtc_set_time(FL_RTC_InitTypeDef *InitStructer) {
    uint8_t n, i;
    uint8_t result;
    FL_RTC_InitTypeDef TempTime1;

    for(n = 0 ; n < 3; n++) {
        FL_RTC_ConfigTime(RTC, InitStructer);
        result = rtc_get_time(&TempTime1);                    //读取确认设置结果
        if (result == 1) {
            continue;
        }
        result = 1;
        for(i = 0; i < 7; i++) {
            if(((uint32_t *)(&TempTime1))[i] != ((uint32_t *)(InitStructer))[i])
            { break; }
        }

        if(i == 7) {
            result = 0;
            break;
        }
    }
    return result;
}

#define DECIMAL_TO_BCD(num) (((num / 10) << 4) | (num % 10))
#define BCD_TO_DECIMAL(bcd) (((bcd) >> 4) * 10 + ((bcd) & 0x0F))

void sys_clock_set_time(uint8_t hour, uint8_t minute, uint8_t second) {
    if (hour > 23 || minute > 59 || second > 59) {
        return ;
    }
    FL_RTC_InitTypeDef   InitTime;
    InitTime.year = 0x20;                                 //在ram中设置写入的时间
    InitTime.month = 1;
    InitTime.day = 1;
    InitTime.week = 1;
    InitTime.hour   = DECIMAL_TO_BCD(hour);
    InitTime.minute = DECIMAL_TO_BCD(minute);
    InitTime.second = DECIMAL_TO_BCD(second);
    rtc_set_time(&InitTime);                                  //将ram的时间 写入RTC模块

    // 写入备份寄存器标记时间有效
    RTC->BKR0 = 0xaa555aa5;
}

// 获取成功返回0
uint8_t sys_clock_get_time(uint8_t* hour, uint8_t* minute, uint8_t* second) {
    FL_RTC_InitTypeDef  rtc_time;
    if (rtc_get_time(&rtc_time) == 0 && rtc_time.year >= 0x20 && RTC->BKR0 == 0xaa555aa5) {
        *hour = BCD_TO_DECIMAL(rtc_time.hour);
        *minute = BCD_TO_DECIMAL(rtc_time.minute);
        *second = BCD_TO_DECIMAL(rtc_time.second);
        return 0;
    }

    return 1;
}

void RTC_IRQHandler(void) {
    //4Hz中断
    if(FL_SET == FL_RTC_IsActiveFlag_4Hz(RTC))  {
        if (timer_4hz_cb) {
            timer_4hz_cb(sys_clock_get_ms());
        }
        FL_RTC_ClearFlag_4Hz(RTC);
    }
}
